////////////////////////////////////////////////////////////////////////////
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//  Portions Copyright (c) Kentaro Sekimoto All rights reserved.
//
////////////////////////////////////////////////////////////////////////////

#include "..\..\..\DeviceCode\Targets\Native\RX62N\DeviceCode\RX62N_EDMAC_lwip\RX62N_EDMAC_lwip.h"
#include "KSZ8041_lwip.h"

static BOOL PhyReset(void)
{
	UINT32 i, data;

	if (!RX62N_EDMAC_lwip_WritePhy(KSZ8041_DEF_ADDR, PHY_REG_BMCR, BMCR_RESET, MII_WR_TOUT))
		return( FALSE );

	HAL_Time_Sleep_MicroSeconds(3);
	for (i = 0; i < MII_WR_TOUT; i++) {
		if (!RX62N_EDMAC_lwip_ReadPhy(KSZ8041_DEF_ADDR, PHY_REG_BMCR, &data, MII_RD_TOUT))
			return( FALSE );
		if (!(data & BMCR_RESET))
			return true;
	}
	return false;
}

static BOOL PhyVerifyId(void)
{
	UINT32 id1,id2;

	if (!RX62N_EDMAC_lwip_ReadPhy(KSZ8041_DEF_ADDR, PHY_REG_IDR1, &id1, MII_RD_TOUT))
		return false;
	if (!RX62N_EDMAC_lwip_ReadPhy(KSZ8041_DEF_ADDR, PHY_REG_IDR2, &id2, MII_RD_TOUT))
		return false;
	if (((id1 << 16) | (id2 & 0xFFF0)) != KSZ8041_ID)
		return false;
	return true;
}

static BOOL PhyAutoNegotiate(void)
{
	UINT32 i, data;

	if (!RX62N_EDMAC_lwip_WritePhy(KSZ8041_DEF_ADDR, PHY_REG_BMCR, PHY_AUTO_NEG, MII_WR_TOUT))
		return false;
	for (i = 0; i < 10; i++) {
		HAL_Time_Sleep_MicroSeconds(1000000);
		RX62N_EDMAC_lwip_ReadPhy(KSZ8041_DEF_ADDR, PHY_REG_BMSR, &data, MII_RD_TOUT);
		if (data & BMSR_AUTO_DONE)
			return true;
	}
	return false;
}

static BOOL PhySetLinkSpeed (void )
{
	UINT32 data;
	BOOL full_duplex, mbit_100;

	if (!PhyAutoNegotiate()) {
		debug_printf("Warning: Ethernet PHY Link autonegotiation failure.\r\n");
		mbit_100 = true;
		full_duplex = true;
	} else {
		if (!RX62N_EDMAC_lwip_ReadPhy(KSZ8041_DEF_ADDR, PHY_REG_BMSR, &data, MII_RD_TOUT))
			return false;
		if (data & STS_FULL_DUP)
			full_duplex = true;
		else
			full_duplex = false;

		if (data & STS_10_MBIT)
			mbit_100 = false;
		else
			mbit_100 = true;
	}
	RX62N_EDMAC_lwip_SetLinkSpeed(mbit_100, full_duplex);
	return true;
}

BOOL ENET_PHY_lwip_init ( void )
{
	if (!PhyReset()) {
		debug_printf("Error: Phy reset\r\n");
		return false;
	}
	if (!PhyVerifyId()) {
		debug_printf("Error: Invalid Phy Id\r\n");
		return false;
	}
	if (!PhySetLinkSpeed()) {
		debug_printf("Error: Link speed failure\r\n");
		return false;
	}
	return true;
}

UINT32 ENET_PHY_lwip_get_MDC_Clk_Div(void)
{
	return 1;
}

BOOL ENET_PHY_lwip_get_MII_mode(void)
{
	return false;
}

BOOL ENET_PHY_lwip_get_link_status(void)
{
	UINT32 data;
	BOOL link_status = false;

	if (RX62N_EDMAC_lwip_ReadPhy(KSZ8041_DEF_ADDR, PHY_REG_STS, &data, MII_RD_TOUT))
		link_status = (data & STS_LINK_ON);
	return link_status;
}

BOOL ENET_PHY_lwip_set_link_speed(void)
{
	return PhySetLinkSpeed();
}
